home *** CD-ROM | disk | FTP | other *** search
/ Workbench Add-On / Workbench Add-On - Volume 1.iso / BBS-Archive / Comm / AmiTCP30b2.lha / src / amitcp / kern / kern_synch.c < prev    next >
C/C++ Source or Header  |  1994-04-02  |  14KB  |  544 lines

  1. RCS_ID_C="$Id: kern_synch.c,v 1.25 1994/04/02 10:29:54 jraja Exp $";
  2. /*
  3.  * Copyright (c) 1993 AmiTCP/IP Group, <amitcp-group@hut.fi>
  4.  *                    Helsinki University of Technology, Finland.
  5.  *                    All rights reserved.
  6.  * 
  7.  * HISTORY
  8.  * $Log: kern_synch.c,v $
  9.  * Revision 1.25  1994/04/02  10:29:54  jraja
  10.  * Changed "wmesg" to const.
  11.  *
  12.  * Revision 1.24  1993/10/07  22:43:02  ppessi
  13.  * Changed AbortIO/Remove to AbortIO/WaitIO
  14.  *
  15.  * Revision 1.23  1993/06/04  11:16:15  jraja
  16.  * Fixes for first public release.
  17.  *
  18.  * Revision 1.22  1993/06/03  20:16:47  too
  19.  * Changed checking of sigIntrMask so that signals both in wakemask and
  20.  * sigIntrMask does not return EINTR but ERESTART
  21.  *
  22.  * Revision 1.21  93/06/03  00:52:51  00:52:51  jraja (Jarno Tapio Rajahalme)
  23.  * Changed the order in which tsleep_main() checks for events: EINTR is tested
  24.  * first, then ERESTART (user signals), then wakeup and finally time out.
  25.  * 
  26.  * Revision 1.20  1993/05/24  19:30:25  ppessi
  27.  * Changed signal handling mechanism in tsleep_main() and WaitSelect()
  28.  *
  29.  * Revision 1.19  1993/05/17  01:07:47  ppessi
  30.  * Changed RCS version.
  31.  *
  32.  * Revision 1.18  1993/04/26  11:54:43  too
  33.  * Changed include paths of amiga_api.h, amiga_libcallentry.h and amiga_raf.h
  34.  * from kern to api
  35.  *
  36.  * Revision 1.17  93/04/20  16:08:03  16:08:03  jraja (Jarno Tapio Rajahalme)
  37.  * The timer request is now ensured to be referenced by the p->tsleep_timer
  38.  * only in the send_timeout.
  39.  * 
  40.  * Revision 1.16  93/04/19  02:23:48  02:23:48  ppessi (Pekka Pessi)
  41.  * Fixed various bugs with tsleep_main().
  42.  * Timeouts and SIGINTR mask seem to work.
  43.  * 
  44.  * Revision 1.15  93/04/13  22:30:59  22:30:59  jraja (Jarno Tapio Rajahalme)
  45.  * Added diagnostic to tsleep() to test that the caller has the
  46.  * syscall_semaphore.
  47.  * 
  48.  * Revision 1.14  93/04/12  00:00:53  00:00:53  jraja (Jarno Tapio Rajahalme)
  49.  * Changed wakeup to use same signal with the timer.
  50.  * Added ULONG *sigmp argument to tsleep_main().
  51.  * 
  52.  * Revision 1.13  93/04/06  15:15:52  15:15:52  jraja (Jarno Tapio Rajahalme)
  53.  * Changed spl function return value storage to spl_t,
  54.  * changed bcopys and bzeros to aligned and/or const when possible,
  55.  * added inclusion of conf.h to every .c file.
  56.  * 
  57.  * Revision 1.12  93/04/05  14:57:45  14:57:45  jraja (Jarno Tapio Rajahalme)
  58.  * Added more efficient spl functions when not debugging.
  59.  * 
  60.  * Revision 1.11  93/03/19  14:14:49  14:14:49  too (Tomi Ollila)
  61.  * Code changes at night 17-18 March 1993
  62.  * 
  63.  * Revision 1.10  93/03/17  12:07:51  12:07:51  jraja (Jarno Tapio Rajahalme)
  64.  * Added more diagnostic to the tsleep().
  65.  * Moved all the work from tsleep_abort_timeout to tsleep_send_timeout.
  66.  * 
  67.  * Revision 1.9  93/03/16  19:45:22  19:45:22  too (Tomi Ollila)
  68.  * Code kludges...(Added zero timeout -> no timeout)
  69.  * 
  70.  * Revision 1.8  93/03/11  20:49:14  20:49:14  jraja (Jarno Tapio Rajahalme)
  71.  * Changed the splsemaphore to spl_semaphore.
  72.  * 
  73.  * Revision 1.7  93/03/07  00:55:23  00:55:23  jraja (Jarno Tapio Rajahalme)
  74.  * Removed redundant assignment of the Wait() result.
  75.  * 
  76.  * Revision 1.6  93/03/05  03:26:16  03:26:16  ppessi (Pekka Pessi)
  77.  * Compiles with SASC. Initial test version.
  78.  * 
  79.  * Revision 1.5  93/02/25  18:39:23  18:39:23  jraja (Jarno Tapio Rajahalme)
  80.  * Moved amiga_includes little upper.
  81.  * 
  82.  * Revision 1.4  93/02/24  12:54:51  12:54:51  jraja (Jarno Tapio Rajahalme)
  83.  * Changed init to remember if initialized.
  84.  * 
  85.  * Revision 1.3  93/02/17  12:54:16  12:54:16  jraja (Jarno Tapio Rajahalme)
  86.  * Broke tsleep apart: tsleep_send_timeout(), tsleep_enter(), tsleep_main()
  87.  * and tsleep_abort_timeout(). See tsleep() for usage.
  88.  * 
  89.  * Revision 1.2  93/02/04  18:28:22  18:28:22  jraja (Jarno Tapio Rajahalme)
  90.  * Added sleep_init(), tsleep() and wakeup().
  91.  * 
  92.  * Revision 1.1  92/12/22  00:08:12  00:08:12  jraja (Jarno Tapio Rajahalme)
  93.  * Initial revision
  94.  */
  95.  
  96. #include <conf.h>
  97.  
  98. #include <sys/param.h>
  99. #include <sys/systm.h>
  100. #include <sys/synch.h>
  101. #include <sys/queue.h>
  102. #include <sys/syslog.h>
  103.  
  104. #include <kern/amiga_includes.h>
  105.  
  106. #include <api/amiga_api.h>
  107.  
  108. /*
  109.  * Note about spl-functions: this implementation does NOT check for software
  110.  * interrupts when returning to level spl 0 (not needed on AmigaOS, see the 
  111.  * bottom of this file).
  112.  */
  113.  
  114. /*
  115.  * Sleeping threads are hashed by 'chan' onto sleep queues.
  116.  */
  117.  
  118. #define    SLEEP_QUEUE_SIZE    32    /* power of 2 */
  119. #define    SLEEP_HASH(x)    (((int)(x)>>5) & (SLEEP_QUEUE_SIZE - 1))
  120.  
  121. queue_head_t    sleep_queue[SLEEP_QUEUE_SIZE];
  122.  
  123. /*
  124.  * semaphore protecting sleep queues
  125.  */
  126. struct SignalSemaphore sleep_semaphore = { 0 };
  127. static BOOL sleep_initialized = FALSE;
  128.  
  129. /*
  130.  * Sleep system initialization.
  131.  */
  132. BOOL
  133. sleep_init(void)
  134. {
  135.   register int i;
  136.  
  137.   if (!sleep_initialized) {
  138.     /*
  139.      * initialize the semaphore protecting sleep queues
  140.      */
  141.     InitSemaphore(&sleep_semaphore);
  142.     
  143.     /*
  144.      * initialize the sleep queues
  145.      */
  146.     for (i = 0; i < SLEEP_QUEUE_SIZE; i++)
  147.       queue_init(&sleep_queue[i]);
  148.     
  149.     sleep_initialized = TRUE;
  150.   }
  151.   return TRUE;
  152. }
  153.  
  154. void
  155. tsleep_send_timeout(struct SocketBase *p,
  156.             const struct timeval *time_out)
  157. {
  158.  
  159.   /*
  160.    * Make sure that the timer message is back from the timer device
  161.    */
  162.   if (p->tsleep_timer->tr_node.io_Message.mn_Node.ln_Type != NT_UNKNOWN) {
  163.     /*
  164.      * abort previous timeout if it has not been completed yet
  165.      */
  166.     if (p->tsleep_timer->tr_node.io_Message.mn_Node.ln_Type != NT_REPLYMSG) {
  167.       AbortIO((struct IORequest *)(p->tsleep_timer));
  168.     }
  169.     /*
  170.      * Remove timerequest from reply port.
  171.      */
  172.     WaitIO((struct IORequest *)p->tsleep_timer);
  173.     /*
  174.      * Set the node type to NT_UNKNOWN to mark that it is referenced only by
  175.      * the p->tsleep_timer.
  176.      */
  177.     p->tsleep_timer->tr_node.io_Message.mn_Node.ln_Type = NT_UNKNOWN;
  178. #if 0
  179.     /*
  180.      * Make sure the signal gets cleared.
  181.      *
  182.      * if this is not done, the tsleep_main will do one unnecessary round
  183.      * IF the signal was set. IS THIS WORTH OF IT?
  184.      */
  185.     SetSignal(0, 1 << p->timerPort->mp_SigBit);
  186. #endif
  187.   }
  188.   /*
  189.    * send timeout request if necessary
  190.    */
  191.   if (time_out) {
  192.     /*
  193.      * set the timeout
  194.      */
  195.     p->tsleep_timer->tr_time = *time_out;
  196.     /*
  197.      * Enable signalling again
  198.      */
  199.     p->timerPort->mp_Flags = PA_SIGNAL;
  200.     /*
  201.      * send the request to the timer device
  202.      */
  203.     BeginIO((struct IORequest *)(p->tsleep_timer));  
  204.   }
  205. }
  206.  
  207. void
  208. tsleep_abort_timeout(struct SocketBase *p,
  209.              const struct timeval *time_out)
  210. {
  211.   if (time_out) {
  212.     /*
  213.      * do not signal us any more
  214.      */
  215.     p->timerPort->mp_Flags = PA_IGNORE;
  216.   }
  217. }
  218.  
  219. void
  220. tsleep_enter(struct SocketBase *p,
  221.          caddr_t chan,        /* 'channel' to wait on */
  222.          const char *wmesg)        /* reason to sleep */
  223. {
  224.   register queue_t    q;
  225.   
  226.   /*
  227.    * Zero is a reserved value, used to indicate
  228.    * that we have been woken up and are no longer on
  229.    * the sleep queues.
  230.    */
  231.   
  232. #if DIAGNOSTIC  
  233.   if (chan == 0)
  234.     panic("tsleep");
  235. #endif
  236.   
  237.   /*
  238.    * The sleep_semaphore protects the sleep queues and
  239.    * p_ fields in SocketBases.
  240.    *
  241.    * When the process is in a sleep queue its p_wchan field is nonzero.
  242.    */
  243.   ObtainSemaphore(&sleep_semaphore);
  244.   p->p_wchan = chan;
  245.   p->p_wmesg = wmesg;
  246.   q = &sleep_queue[SLEEP_HASH(chan)];
  247.   queue_enter(q, p, struct SocketBase *, p_sleep_link);
  248.   ReleaseSemaphore(&sleep_semaphore);
  249. }
  250.  
  251. int
  252. tsleep_main(struct SocketBase *p, ULONG wakemask)
  253. {
  254.   ULONG sigmask, bmask, timermask;
  255.   struct timerequest *timerReply;
  256.   register queue_t q;
  257.   int result;
  258.  
  259.   /* 
  260.    * Set the signal mask for the wait
  261.    */
  262.   timermask = 1 << p->timerPort->mp_SigBit;
  263.   sigmask = timermask | p->sigIntrMask | wakemask;
  264.  
  265.   for (;;) {
  266.     /* 
  267.      * wait for timeout, wakeup or interrupt
  268.      */
  269.     bmask = Wait(sigmask);
  270.  
  271.     /*
  272.      * Check if we were interrupted
  273.      */
  274.     if (bmask & p->sigIntrMask & ~wakemask) {
  275.       result = EINTR;
  276.       break;
  277.     }
  278.  
  279.     /*
  280.      * Check for user signals
  281.      */
  282.     if (bmask & wakemask) {
  283.       result = ERESTART;
  284.       break;
  285.     }
  286.  
  287.     /*
  288.      * check if we were woken up. 
  289.      *
  290.      * If p->p_chan is zero then the wakener has removed us from
  291.      * the sleep queue.
  292.      */
  293.     if (p->p_wchan == 0) {
  294.       /*
  295.        * Set back the signals which interrupted us so that user program can
  296.        * detect them
  297.        */
  298.       bmask &= p->sigIntrMask|wakemask;
  299.       if (bmask)
  300.     SetSignal(bmask, bmask);
  301.       
  302.       return 0;            /* return success */
  303.     }
  304.  
  305.     /*
  306.      * check if we got the timer reply signal and message
  307.      */
  308.     if (bmask & timermask &&
  309.     (timerReply = (struct timerequest *)GetMsg(p->timerPort)) && 
  310.     timerReply == p->tsleep_timer) { /* sanity check */
  311.       /*
  312.        * timeout expired.
  313.        *
  314.        * Set the node type to NT_UNKNOWN to mark that it is referenced only by
  315.        * the p->tsleep_timer.
  316.        */
  317.       timerReply->tr_node.io_Message.mn_Node.ln_Type = NT_UNKNOWN;
  318.  
  319.       result = EWOULDBLOCK;
  320.       break;
  321.     }
  322.     
  323.   } /* for */
  324.  
  325.   /* Return path when sleeper has to be removed from the sleep queue */
  326.  
  327.   /*
  328.    * Set back the signals which interrupted us so that user program can
  329.    * detect them
  330.    */
  331.   bmask &= p->sigIntrMask | wakemask;
  332.   if (bmask)
  333.     SetSignal(bmask, bmask);
  334.  
  335.   /*
  336.    * remove from the sleep queue
  337.    */
  338.   ObtainSemaphore(&sleep_semaphore);
  339.   /*
  340.    * If p_chan is nonzero then we still are on the sleep queue and
  341.    * need to be removed from there.
  342.    */
  343.   if (p->p_wchan != 0) {
  344.     q = &sleep_queue[SLEEP_HASH(p->p_wchan)];
  345.     p->p_wchan = (char *)0;
  346.     queue_remove(q, p, struct SocketBase *, p_sleep_link);
  347.   }
  348.   ReleaseSemaphore(&sleep_semaphore);
  349.  
  350.   return result;
  351. }
  352.  
  353. /*
  354.  * General sleep call. 
  355.  * NOTE: caller is assumed to hold the syscall_semaphore!         \* XXX *\
  356.  * Suspends current process until a wakeup is made on chan.
  357.  * Sleeps at most the time specified in a time_out (NULL means no timeout).
  358.  * Lowers the current spl-level to 0 while in sleep.
  359.  * Returns 0 if awakened, EWOULDBLOCK if the timeout expires and
  360.  * EINTR if interrupted.
  361.  */
  362. int
  363. tsleep(struct SocketBase *p,  /* Library base through which this call came */
  364.        caddr_t chan,          /* 'channel' to wait on */
  365.        const char *wmesg,          /* reason to sleep */
  366.        const struct timeval *time_out) /* timeout as timeval structure */
  367. {
  368.   int result;
  369.   spl_t old_spl;
  370.  
  371. #if DIAGNOSTIC
  372.   extern struct Task *AmiTCP_Task;
  373.   if (FindTask(NULL) == AmiTCP_Task) {
  374.     log(LOG_ERR, "AmiTCP did tsleep() itself!");
  375.     return (-1);
  376.   }
  377. #endif 
  378.   
  379. #if DIAGNOSTIC
  380.   if (p == NULL) {
  381.     log(LOG_ERR, "tsleep() called with NULL SocketBase pointer!");
  382.     return (-1);
  383.   }
  384. #endif 
  385.  
  386. #if DIAGNOSTIC
  387.   if (SysBase->ThisTask != syscall_semaphore.ss_Owner) {
  388.     log(LOG_ERR, "tsleep() called with NO syscall_semaphore!");
  389.     return (-1);
  390.   }
  391. #endif
  392.     
  393.   tsleep_send_timeout(p, time_out);
  394.   
  395.   tsleep_enter(p, chan, wmesg);
  396.  
  397.   /*
  398.    * release spl-level while in sleep.
  399.    * 
  400.    * NOTE: syscall_semaphore must be freed as well!
  401.    */
  402.  
  403.   old_spl = spl0();
  404.   ReleaseSemaphore(&syscall_semaphore);                         /* XXX */
  405.  
  406.   result = tsleep_main(p, 0);
  407.  
  408.   /*
  409.    * return old spl-level
  410.    */
  411.   ObtainSemaphore(&syscall_semaphore);                         /* XXX */
  412.   splx(old_spl);
  413.  
  414.   /*
  415.    * abort the timeout request if necessary
  416.    */
  417.   if (result != EWOULDBLOCK) 
  418.     tsleep_abort_timeout(p, time_out);
  419.  
  420.   return (result);
  421. }
  422.  
  423. void
  424. wakeup(caddr_t chan)
  425. {
  426.   register queue_t q;
  427.   struct SocketBase *p, *next;
  428.   
  429. #if DIAGNOSTIC
  430.   if (chan == 0) {
  431.     log(LOG_ERR, "wakeup on chan zero");
  432.     return;
  433.   }
  434. #endif 
  435.  
  436.   ObtainSemaphore(&sleep_semaphore);
  437.   q = &sleep_queue[SLEEP_HASH(chan)];
  438.   
  439.   p = (struct SocketBase *)queue_first(q);
  440.   while (!queue_end(q, (queue_entry_t)p)) {
  441.     next = (struct SocketBase *)queue_next(&p->p_sleep_link);
  442.     if (p->p_wchan == chan) {
  443.       /*
  444.        * mark sleeper as woken up
  445.        */
  446.       p->p_wchan = NULL;
  447.       /*
  448.        * remove sleeper from the sleep queue
  449.        */
  450.       queue_remove(q, p, struct SocketBase *, p_sleep_link);
  451.       /*
  452.        * signal process to take attention
  453.        */
  454.       Signal(p->thisTask, 1 << p->timerPort->mp_SigBit);
  455.     }
  456.     p = next;
  457.   }
  458.   ReleaseSemaphore(&sleep_semaphore);
  459. }
  460.  
  461. /*
  462.  * Spl-level emulation:
  463.  *
  464.  * In this implementation the processor priority levels are modelled
  465.  * either with one semaphore (ifdef DEBUG) or by Exec Task switch
  466.  * disabling feature. Semaphore is used while debugging for security (to
  467.  * be able to single step almost everywhere). The production version uses
  468.  * ExecBase's TDNestCnt for speed (prevent unnecessary task switches).
  469.  * 
  470.  * Note that both ways lead to the fact that when someone sets, say, splimp()
  471.  * he will WAIT for the holder of splnet() to finish.
  472.  */
  473.  
  474. #ifdef DEBUG
  475. /*
  476.  * spl_semaphore is used as mutex for all spl-levels
  477.  *
  478.  * Note that InitSemaphore() requires the signalSemaphore to be initialized
  479.  * to zero (here done statically).
  480.  */
  481. struct SignalSemaphore spl_semaphore = { 0 };
  482.  
  483. /*
  484.  * spl_level holds the current pseudo priority level.
  485.  * NOTE: this may be accessed only while holding the spl_semaphore.
  486.  */
  487. spl_t spl_level = SPL0;
  488. static BOOL spl_initialized = FALSE;
  489.  
  490. BOOL
  491. spl_init(void)
  492. {
  493.   if (!spl_initialized) {
  494.     /*
  495.      * Initialize spl_semaphore for use. After this call any number of 
  496.      * tasks may use spl-functions.
  497.      */
  498.     InitSemaphore(&spl_semaphore);
  499.     spl_initialized = TRUE;
  500.   }
  501.   return TRUE;
  502. }
  503.  
  504. spl_t
  505. spl_n(spl_t new_level)
  506. {
  507.   register spl_t old_level;
  508.  
  509.   ObtainSemaphore(&spl_semaphore);
  510.   old_level = spl_level;
  511.   spl_level = new_level;
  512.   
  513.   if (new_level > old_level) {    /* raise level */
  514.     if (old_level == 0)        /* lock when raising over zero */
  515.       /* 
  516.        * so. return without releasing the lock
  517.        */
  518.       return old_level;
  519.   }
  520.   else if (new_level < old_level) {    /* lower level */
  521.     if (new_level == 0)            /* unlock when lowering to zero */
  522.       /*
  523.        * now release the lock kept above
  524.        */
  525.       ReleaseSemaphore(&spl_semaphore);
  526.   }
  527.   ReleaseSemaphore(&spl_semaphore);
  528.  
  529.   return old_level;
  530. }
  531.  
  532. #else
  533.  
  534. BOOL
  535. spl_init(void)
  536. {
  537.   return TRUE;
  538. }
  539.  
  540. /*
  541.  * spl_n is defined as an inline in <sys/synch.h>
  542.  */
  543. #endif /* DEBUG */
  544.